package com.atguigu.gmall.search.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.atguigu.gmall.search.pojo.Goods;
import com.atguigu.gmall.search.pojo.SearchParamVo;
import com.atguigu.gmall.search.pojo.SearchResponseAttrVo;
import com.atguigu.gmall.search.pojo.SearchResponseVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author: 86182
 * @Date: 2021/8/6
 * @Time: 23:22
 * @Version:
 */
@Service
public class SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    
    public SearchResponseVo search(SearchParamVo paramVo) {
        try {
            SearchRequest searchRequest = new SearchRequest(new String[]{"goods"},this.buildDsl(paramVo));
            SearchResponse response = this.restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // TODO：解析搜索结果集 response --> SearchResponseVo
           SearchResponseVo responseVo = this.parseResult(response);
            //获取pageNum
            responseVo.setPageNum(paramVo.getPageNum());
            //获取pageSize
            responseVo.setPageSize(paramVo.getPageSize());
            return responseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 把response --> SearchResponseVo
     * @param response
     * @return
     */
    private SearchResponseVo parseResult(SearchResponse response) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        //Hits
        SearchHits hits = response.getHits();
        //获取总记录数
        searchResponseVo.setTotal(hits.getTotalHits());

        //获取当前页的数据

        List<Goods> goodsList = Arrays.stream(hits.getHits()).map(hitHit -> {
            String json = hitHit.getSourceAsString();
            Goods goods = JSON.parseObject(json, Goods.class);
            // 获取高亮结果集,覆盖掉普通的title
            Map<String, HighlightField> highlightFields = hitHit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            goods.setTitle(title.fragments()[0].string());
            return goods;
        }).collect(Collectors.toList());
        searchResponseVo.setGoodsList(goodsList);
        //获取聚合结果集aggregation
        Aggregations aggregations = response.getAggregations();
        //获取品牌聚合结果集
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> brandIdBuckets = brandIdAgg.getBuckets();
        //把桶集合转化成品牌的集合
        if (!CollectionUtils.isEmpty(brandIdBuckets)){
            searchResponseVo.setBrands(brandIdBuckets.stream().map(bucket -> {
                BrandEntity brandEntity = new BrandEntity();
                brandEntity.setId(bucket.getKeyAsNumber().longValue());
                // 解析brandNameAgg 获取品牌名称
                ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
                List<? extends Terms.Bucket> brandNameBuckets = brandNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(brandNameBuckets)){
                    brandEntity.setName(brandNameBuckets.get(0).getKeyAsString());
                }
                // 解析品牌logoAgg 获取 品牌logo
                ParsedStringTerms logoAgg = bucket.getAggregations().get("logoAgg");
                List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                if (!CollectionUtils.isEmpty(logoAggBuckets)){
                    brandEntity.setLogo(logoAggBuckets.get(0).getKeyAsString());
                }
                return brandEntity;
            }).collect(Collectors.toList()));
        }

        //解析分类聚合结果集 转化成 分类列表
        ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
        List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(categoryIdAggBuckets)){
            searchResponseVo.setCategories(categoryIdAggBuckets.stream().map(bucket -> {
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(bucket.getKeyAsNumber().longValue());
                // 获取分类名称的子聚合 解析出分类名称
                ParsedStringTerms categoryNameAgg = bucket.getAggregations().get("categoryNameAgg");
                List<? extends Terms.Bucket> buckets = categoryNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(buckets)){
                    categoryEntity.setName(buckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }

        //解析规格参数的聚合结果集
        ParsedNested attrAgg = aggregations.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> buckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(buckets)){
            List<SearchResponseAttrVo> attrVos = buckets.stream().map(bucket -> {
                SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
                searchResponseAttrVo.setAttrId(bucket.getKeyAsNumber().longValue());
                // 解析attrNameAgg 获取规格参数名称
                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrNameAggBuckets)) {
                    searchResponseAttrVo.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
                }
                // 解析attrValueAgg获取规格参数的值列表
                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
                List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrValueAggBuckets)) {
                    List<String> attrValues = attrValueAggBuckets.stream().map(valuleBucket -> valuleBucket.getKeyAsString()).collect(Collectors.toList());
                    searchResponseAttrVo.setAttrValues(attrValues);
                }
                return searchResponseAttrVo;
            }).collect(Collectors.toList());
            searchResponseVo.setFilters(attrVos);
        }

        return searchResponseVo;
    }


    /**
     * 构建搜索条件
     * @param paramVo
     * @return
     */
    private SearchSourceBuilder buildDsl(SearchParamVo paramVo) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        String keyword = paramVo.getKeyword();
        if (StringUtils.isBlank(keyword)){
            // TODO：打广告
            return sourceBuilder;
        }
        //1. 构建搜索查询及过滤条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQueryBuilder);
        //1.1 匹配查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        //1.2 过滤条件
        //1.2.1 品牌过滤
        List<Long> brandId = paramVo.getBrandId();
        if (!CollectionUtils.isEmpty(brandId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",brandId));
        }

        //1.2.2 分类过滤
        List<Long> categoryId = paramVo.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId",categoryId));
        }
        //1.2.3 价格范围过滤
        Double priceFrom = paramVo.getPriceFrom();
        Double priceTo = paramVo.getPriceTo();
        // 如果价格区间中任何一个参数不为空都要添加价格区间过滤
        if (priceFrom != null || priceTo != null){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            boolQueryBuilder.filter(rangeQuery);
            if (priceFrom != null){
                rangeQuery.gte(priceFrom);
            }
            if (priceTo != null){
                rangeQuery.lte(priceTo);
            }
        }

        //1.2.4 是否有货过滤
        Boolean store = paramVo.getStore(); // 京东: 只要store不为空,就查有货
        if (store != null){  //可查有货 无货
            boolQueryBuilder.filter(QueryBuilders.termQuery("store",store));
        }

        //1.2.5 规格参数的过滤: [4:8G-12G-6G, 5:128G-256G]
        List<String> props = paramVo.getProps();
        if (!CollectionUtils.isEmpty(props)){
            props.forEach(prop ->{
                // 添加具体查询条件
                String[] attr = StringUtils.split(prop, ":");
                if (attr != null || attr.length == 2){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None);
                    boolQueryBuilder.filter(nestedQueryBuilder);
                    // 嵌套查询中主要是bool查询,bool查询中主要是两个词条插询
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId",attr[0]));
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue", StringUtils.split(attr[1], "-")));
                }
            });
        }
        //2. 构建排序条件
        Integer sort = paramVo.getSort();
        if (sort != null){ //1-价格降序 2-价格升序 3-销量降序 4-新品降序
            switch (sort){
                case 1: sourceBuilder.sort("price", SortOrder.DESC);
                    break;
                case 2: sourceBuilder.sort("price",SortOrder.ASC);
                    break;
                case 3: sourceBuilder.sort("sales",SortOrder.DESC);
                    break;
                case 4: sourceBuilder.sort("createTime",SortOrder.DESC);
                    break;
                default: sourceBuilder.sort("_score",SortOrder.DESC);
                    break;
            }
        }
        //3. 分页
        Integer pageNum = paramVo.getPageNum();
        Integer pageSize = paramVo.getPageSize();
        sourceBuilder.from((pageNum-1) * pageSize);
        sourceBuilder.size(pageSize);
        //4. 高亮
        sourceBuilder.highlighter(new HighlightBuilder()
                    .field("title")
                    .preTags("<font style='color:red'>")
                    .postTags("</font>"));
        //5. 聚合
        //5.1 品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                        .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                        .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));

        //5.2 分类聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        //5.3 规格参数聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                    .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                    .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                    .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));

        //结果集过滤
        sourceBuilder.fetchSource(new String[]{"skuId", "title", "subTitle", "defaultImage", "price"}, null);        System.out.println(sourceBuilder);
        return sourceBuilder;
    }
}
